/**
Company: Sarkar software technologys
WebSite: http://www.sarkar.cn
Author: yeganaaa
Date : 3/2/18
Time: 3:26 PM
 */

package Cn.Sarkar.MMS.Entities.DataSources

import Cn.Sarkar.Configuration.makeConfig
import Cn.Sarkar.Entities.Common.ConnectionPool
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.Extensions.isExistsTableOf
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.IQueriableCollection
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.IDataContext
import Cn.Sarkar.MMS.DataBaseDefinations.Additional.*
import Cn.Sarkar.MMS.DataBaseDefinations.Independent.*
import Cn.Sarkar.MMS.DataBaseDefinations.Independent.Permission.PermissionGroups
import Cn.Sarkar.MMS.DataBaseDefinations.Independent.Permission.PermissionItems
import Cn.Sarkar.MMS.DataBaseDefinations.Independent.Permission.Permissions
import Cn.Sarkar.MMS.DataBaseDefinations.Independent.Wallet.ChangeHistorys
import Cn.Sarkar.MMS.DataBaseDefinations.Independent.Wallet.MemberShipLevels
import Cn.Sarkar.MMS.DataBaseDefinations.Independent.Wallet.Wallets
import Cn.Sarkar.MMS.Entities.Additional.*
import Cn.Sarkar.MMS.Entities.DataSources.Sources.MySqlDataSource
import Cn.Sarkar.MMS.Entities.DataSources.Sources.SqLiteDataSource
import Cn.Sarkar.MMS.Entities.Independent.*
import Cn.Sarkar.MMS.Entities.Independent.Permission.Permission
import Cn.Sarkar.MMS.Entities.Independent.Permission.PermissionGroup
import Cn.Sarkar.MMS.Entities.Independent.Permission.PermissionItem
import Cn.Sarkar.MMS.Entities.Independent.Wallet.ChangeHistory
import Cn.Sarkar.MMS.Entities.Independent.Wallet.MemberShipLevel
import Cn.Sarkar.MMS.Entities.Independent.Wallet.Wallet
import kotlinx.coroutines.experimental.runBlocking
import java.io.File


//var defaultConnection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3308/SarkarMMS", "yeganaaa", "Developer653125")


interface IDataSource : IDataContext {
//class MySqlDataSource : SQLiteDataContext(DriverManager.getConnection("jdbc:sqlite:SarkarMMS.db")) {

    val contacts: IQueriableCollection<Contact>
    val countries: IQueriableCollection<Country>
    val descriptions: IQueriableCollection<Description>
    val names: IQueriableCollection<Name>
    val thirdPartyAccounts: IQueriableCollection<ThirdPartyAccount>
    val types: IQueriableCollection<Type>

    val permissionGroups: IQueriableCollection<PermissionGroup>
    val permissionItems: IQueriableCollection<PermissionItem>
    val permissions: IQueriableCollection<Permission>

    val changeHistorys: IQueriableCollection<ChangeHistory>
    val memberShipLevels: IQueriableCollection<MemberShipLevel>
    val wallets: IQueriableCollection<Wallet>

    val accounts: IQueriableCollection<Account>
    val applications: IQueriableCollection<Application>
    val applicationDomains: IQueriableCollection<ApplicationDomain>
    val companies: IQueriableCollection<Company>
    val connectApplicationAndAccountAndWallet: IQueriableCollection<DomainRelation>
    val persons: IQueriableCollection<Person>

    companion object {
        internal val AllTables = arrayOf(
                Contacts,
                Countries,
                Descriptions,
                Names,
                ThirdPartyAccounts,
                Types,

                PermissionGroups,
                PermissionItems,
                Permissions,

                ChangeHistorys,
                MemberShipLevels,
                Wallets,

                Accounts,
                ApplicationDomains,
                Applications,
                Companies,
                DomainRelations,
                Persons
        )

        internal val configFile = "Configuration${File.separator}MMSDataConfigurations.json"
        private var configConstructor = {
            ConnectionPool.Configuration(
                    database = "mysql",
                    driverClass = "com.mysql.jdbc.Driver",
                    userName = "root",
                    password = "12345678",
                    jdbcUrl = "jdbc:mysql://127.0.0.1:3306/SarkarMMS"
            )
        }
        fun loadNewConfig() = makeConfig(configFile, ConnectionPool.Configuration::class, configConstructor)

        private var _dataBaseConfig: ConnectionPool.Configuration? = null
        var dataBaseConfig: ConnectionPool.Configuration
            get() {
                if (_dataBaseConfig == null) {
                    _dataBaseConfig = makeConfig(configFile, ConnectionPool.Configuration::class, configConstructor)
                }
                return _dataBaseConfig!!
            }
            set(value) {
                File(configFile).run {
                    if (exists()) delete()
                }
                val config = makeConfig(configFile, ConnectionPool.Configuration::class, { value })
                _dataBaseConfig = value
                pool.close()
                pool = ConnectionPool(config)
            }

        private var _pool: ConnectionPool? = null
        internal var pool: ConnectionPool
            get() {
                if (_pool == null) {
                    _pool = ConnectionPool(dataBaseConfig)
                }
                return _pool!!
            }
            set(value) {
                _pool = value
            }

        fun <T> openAndAutoClose(connectionPool: ConnectionPool = pool, action: suspend (dataSource: IDataSource) -> T): T {
            val db: IDataSource = when (connectionPool.config.database.toLowerCase()) {
                "mysql" -> MySqlDataSource(connectionPool.getConnection())
                "sqlite" -> SqLiteDataSource(connectionPool.getConnection())

                else -> throw Exception("""Not supported the database system: "${connectionPool.config.database}", please change database, نۆەتتىكى سىز تەڭشىگەن ساندان ماتورىنى پروگرامما قوللىمايدۇ، پروگراممىنى تارقاتقۇچى بىلەن ئالاقىلىشىڭ!""")
            }

            val result = runBlocking { action(db) }
            db.closeConnection()
            return result
        }

        val isTablesExists: Boolean get() = IDataSource.openAndAutoClose { it.isExistsTableOf(Persons) }
    }
}